package cn.org.xwb.common.utils;

import lombok.extern.log4j.Log4j2;

import java.util.Objects;
import java.util.Random;

/**
 * 和随机 random 相关的工具类
 */
@Log4j2
public class RandomUtil {

    /**
     * 十进制数字字符组成的数组
     */
    private final static char[] decimalChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

    /**
     * 小写英文字符组成的数组
     */
    private final static char[] lowerChars = {
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
            'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
            'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
            'y', 'z'
    };

    /**
     * 十进制数字字符、大写英文字符、小写英文字符组成的数组
     */
    private final static char[] decimalLowerUpperChars = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
            'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
            'U', 'V', 'W', 'X', 'Y', 'Z'
    };

    private RandomUtil() {
    }

    /**
     * 生成 0(包含) - bound(不包含) 之间的随机 int 整数
     *
     * @param bound 生成的随机 int 整数的上限(不包含)
     * @return 0(包含) - bound(不包含) 之间的随机 int 整数
     */
    public static int genRandInt02BoundExcluded(int bound) {
        return new Random().nextInt(bound);
    }

    /**
     * 生成 0(包含) - bound(包含) 之间的随机 int 整数
     *
     * @param bound 生成的随机 int 整数的上限(包含)
     * @return 0(包含) - bound(包含) 之间的随机 int 整数
     */
    public static int genRandInt02Bound(int bound) {
        if (bound <= 0) {
            log.warn(Constant.getWarnTipMsg_gt0("bound"));
            return -1;
        }
        return genRandInt02BoundExcluded(bound + 1);
    }

    /**
     * 生成指定范围 [origin(包含) - bound(不包含)] 之间的随机 int 整数
     *
     * @param origin 生成的随机 int 整数的下限(包含)
     * @param bound  生成的随机 int 整数的上限(不包含)
     * @return [origin(包含) - bound(不包含)] 之间的随机 int 整数
     */
    public static int genRandIntOrigin2BoundExcluded(int origin, int bound) {
        return new Random().nextInt(origin, bound);
    }

    /**
     * 生成指定范围 [origin(包含) - bound(包含)] 之间的随机 int 整数
     *
     * @param origin 生成的随机 int 整数的下限(包含)
     * @param bound  生成的随机 int 整数的上限(包含)
     * @return [origin(包含) - bound(包含)] 之间的随机 int 整数
     */
    public static int genRandIntOrigin2Bound(int origin, int bound) {
        return genRandIntOrigin2BoundExcluded(origin, bound + 1);
    }

    /**
     * 根据给定的字符数组和随机字符串的指定长度，生成指定长度的随机字符串
     *
     * @param chars        生成指定长度的随机字符串中每个字符的来源
     * @param randStrLen 随机字符串的指定长度
     * @return 指定长度的随机字符串
     */
    public static String genRandStrByChars(char[] chars, int randStrLen) {
        // 给定的字符数组为空时，输出警告，并直接退出函数
        if (chars == null || chars.length == 0) {
            log.warn(Constant.getWarnTipMsg("chars", "不为空 (不为 null，且数组长度大于 0)，已停止执行方法"));
            return "";
        }
        // 指定的要生成的随机字符串长度小于等于 0，输出警告，并直接退出函数
        if (randStrLen <= 0) {
            log.warn(Constant.getWarnTipMsg_gt0("randStrLen"));
            return "";
        }
        // 随机字符串生成过程中的字符串
        StringBuilder randomStr = new StringBuilder();
        // 生成指定长度的随机字符串
        for (int i = 0; i < randStrLen; i++) {
            // 每次从给定的字符数组中随机取出一个字符
            randomStr.append(chars[genRandInt02BoundExcluded(chars.length)]);
        }
        return randomStr.toString();
    }

    /**
     * 生成指定长度的随机数字符串
     *
     * @param randNumStrLen 随机数字符串的长度
     * @return 指定长度的随机数字符串
     */
    public static String genRandNumStr(int randNumStrLen) throws RuntimeException {
        // 指定的要生成的随机数字符串长度小于等于 0，输出警告，并直接退出函数
        if (randNumStrLen <= 0) {
            log.warn(Constant.getWarnTipMsg_gt0("randNumStrLen"));
            return "";
        }
        // 生成指定长度的随机数字符串
        return genRandStrByChars(decimalChars, randNumStrLen);
    }

    /**
     * 生成指定长度的小写英文字符串
     *
     * @param randLowerStrLen 小写英文字符串的长度
     * @return 指定长度的小写英文字符串
     */
    public static String genRandLowerStr(int randLowerStrLen) throws RuntimeException {
        // 指定的要生成的字符串长度小于等于 0，输出警告，并直接退出函数
        if (randLowerStrLen <= 0) {
            log.warn(Constant.getWarnTipMsg_gt0("randLowerStrLen"));
            return "";
        }
        // 生成指定长度的小写英文字符串
        return genRandStrByChars(lowerChars, randLowerStrLen);
    }

    /**
     * 生成指定长度的大写英文字符串
     *
     * @param randUpperStrLen 大写英文字符串的长度
     * @return 指定长度的大写英文字符串
     */
    public static String genRandUpperStr(int randUpperStrLen) throws RuntimeException {
        // 指定的要生成的字符串长度小于等于 0，输出警告，并直接退出函数
        if (randUpperStrLen <= 0) {
            log.warn(Constant.getWarnTipMsg_gt0("randUpperStrLen"));
            return "";
        }
        // 生成指定长度的大写英文字符串
        return genRandLowerStr(randUpperStrLen).toUpperCase();
    }

    /**
     * 生成指定长度的由十进制数字、大写、小写英文字符组成的随机字符串
     *
     * @param randNumLowerUpperStrLen 随机字符串的长度
     * @return 指定长度的由十进制数字、大写、小写英文字符组成的随机字符串
     */
    public static String genRandNumLowerUpperStr(int randNumLowerUpperStrLen) throws RuntimeException {
        // 指定的要生成的字符串长度小于等于 0，输出警告，并直接退出函数
        if (randNumLowerUpperStrLen <= 0) {
            log.warn(Constant.getWarnTipMsg_gt0("randNumLowerUpperStrLen"));
            return "";
        }
        // 生成指定长度的由十进制数字、大写、小写英文字符组成的随机字符串
        return genRandStrByChars(decimalLowerUpperChars, randNumLowerUpperStrLen);
    }

    /**
     * 生成指定长度的由十进制数字、大写英文字符组成的随机字符串
     *
     * @param randNumUpperStrLen 随机字符串的长度
     * @return 指定长度的由十进制数字、大写英文字符组成的随机字符串
     */
    public static String genRandNumUpperStr(int randNumUpperStrLen) throws RuntimeException {
        // 指定的要生成的字符串长度小于等于 0，输出警告，并直接退出函数
        if (randNumUpperStrLen <= 0) {
            log.warn(Constant.getWarnTipMsg_gt0("randNumUpperStrLen"));
            return "";
        }
        // 生成指定长度的由十进制数字、大写英文字符组成的随机字符串
        return genRandNumLowerUpperStr(randNumUpperStrLen).toUpperCase();
    }

    /**
     * 生成指定长度的由十进制数字、小写英文字符组成的随机字符串
     *
     * @param randNumLowerStrLen 随机字符串的长度
     * @return 指定长度的由十进制数字、小写英文字符组成的随机字符串
     */
    public static String genRandNumLowerStr(int randNumLowerStrLen) throws RuntimeException {
        // 指定的要生成的字符串长度小于等于 0，输出警告，并直接退出函数
        if (randNumLowerStrLen <= 0) {
            log.warn(Constant.getWarnTipMsg_gt0("randomNumLowerStrLen"));
            return "";
        }
        // 生成指定长度的由十进制数字、小写英文字符组成的随机字符串
        return genRandNumLowerUpperStr(randNumLowerStrLen).toLowerCase();
    }

    /**
     * 和随机工具类相关的常量
     */
    public static class Constant {

        private Constant() {}

        /**
         * 警告提示信息
         */
        public static final String warnTipMsg = "参数 %s 的取值应当%s";

        /**
         * 获取填充完成后的参数传值不正确的警告提示信息
         *
         * @param argName 参数名
         * @param tip 正确的取值提示信息
         * @return 填充完成后的参数传值不正确的警告提示信息
         */
        public static String getWarnTipMsg(String argName, String tip) {
            if (Objects.isNull(argName) || argName.isEmpty()) {
                log.warn(warnTipMsg.formatted("argName", "不为空(不为 null 或长度小于等于 0)"));
                return "";
            }
            if (Objects.isNull(tip) || tip.isEmpty()) {
                log.warn(warnTipMsg.formatted("tip", "不为空(不为 null 或长度小于等于 0)"));
                return "";
            }
            return warnTipMsg.formatted(argName, tip);
        }

        /**
         * 获取填充完成后的参数传值应该大于 0 的警告提示信息
         *
         * @param argName 参数名
         * @return 填充完成后的参数传值应该大于 0 的警告提示信息
         */
        public static String getWarnTipMsg_gt0(String argName) {
            return getWarnTipMsg(argName, "大于 0，已停止执行方法");
        }

    }

}
